package com.zt.proxy.service.impl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.validation.constraints.NotNull;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.fastjson.JSON;
import com.zt.proxy.component.DependencyFactory;
import com.zt.proxy.dto.PomDTO;
import com.zt.proxy.dto.ResultDTO;
import com.zt.proxy.exception.ServiceException;
import com.zt.proxy.model.PomModel;
import com.zt.proxy.service.PomService;
import com.zt.proxy.util.StringUtil;
import com.zt.proxy.util.ThreadPoolContainer;

import lombok.extern.slf4j.Slf4j;

@Service("pomService")
@Slf4j
public class PomServiceImpl implements PomService {
    
    private Lock locker = new ReentrantLock();
    
    @Autowired
    private DependencyFactory dependencyFactory;

    @Value("${proxy.dependency.pom}")
    private String pomPath;
    
    @Value("${proxy.dependency.lib}")
    private String libPath;

    @Override
    public ResultDTO<PomDTO> invoke(PomDTO dto) throws Exception {
        
        log.info("begin to parse the pom");
        final PomModel model = parsePom(dto.getPom());
        
        checkModel(model);
        
        try {

            locker.lock();

            log.info("begin to append the parse content");
            appendPom(dto.getPom(), pomPath);

            log.info("fork another thread to download jars.");

            try {
                dependencyFactory.downLoadMavenJar(model);
            } catch (Exception e) {
                log.error("download jars occur an error:{}", e);
            }

        } finally {
            locker.unlock();
        }
        return ResultDTO.createSuccessResult("success", dto, PomDTO.class);
    }

    @Override
    public PomModel parsePom(@NotNull String xml) throws DocumentException{

        Document doc = DocumentHelper.parseText(xml);
        Element rootElement = doc.getRootElement();
        Element groupIdEl = rootElement.element("groupId");
        Element artifactIdEl = rootElement.element("artifactId");
        Element versionEl = rootElement.element("version");
        PomModel model = new PomModel();
        model.setArtifactId(artifactIdEl == null ? "" : artifactIdEl.getStringValue());
        model.setGroupId(groupIdEl == null ? "" : groupIdEl.getStringValue());
        model.setVersion(versionEl == null ? "" : versionEl.getStringValue());
        return model;
    }

    @Override
    public void appendPom(@NotNull String pomContent, String pomPath) throws Exception {
        
        Document contentDoc = DocumentHelper.parseText(pomContent);
        Element deEl = contentDoc.getRootElement();
        
        String path = (StringUtils.isEmpty(pomPath)) ? this.pomPath : pomPath;
        File file = new File(path);
        SAXReader reader = new SAXReader();
        Document doc = reader.read(file);
        Element root = doc.getRootElement();
        Element el = root.element("dependencies");
        
        el.add(deEl);
        
        writerDocumentToFile(doc, path);
        
    }

    @Override
    public List<PomModel> listDependencies(PomDTO dto) throws DocumentException {
        List<PomModel> result = new ArrayList<>();

        String path = (StringUtils.isEmpty(dto.getPath())) ? this.pomPath : dto.getPath();
        File file = new File(path);

       
        //读取文件 转换成Document
        SAXReader reader = new SAXReader();
        Document doc = reader.read(file);
        Element root = doc.getRootElement();
        Element el = root.element("dependencies");
        List<Element> deps = el.elements();

        deps.forEach(dep -> {
            PomModel model = new PomModel();
            model.setGroupId(dep.element("groupId").getStringValue());
            model.setArtifactId(dep.element("artifactId").getStringValue());
            model.setVersion(dep.element("version").getStringValue());

            result.add(model); 
        });
        
        return result;

    }
    
    private void checkModel(PomModel model) {
      if (model.isBroken()) {
          throw new ServiceException(StringUtil.format("The content of pom is Incomplete.[{}]", JSON.toJSONString(model)));
       }

    }
    
    public void writerDocumentToFile(Document document, String path)throws Exception{
        //输出格式
        OutputFormat format = OutputFormat.createPrettyPrint();
        //设置编码
        format.setEncoding("UTF-8");
        //XMLWriter 指定输出文件以及格式
        XMLWriter writer = new XMLWriter(new OutputStreamWriter(new FileOutputStream(new File(path)),"UTF-8"), format);
        
        //写入新文件
        writer.write(document);
        writer.flush();
        writer.close();
    }

    @Override
    public ResultDTO<String> loadJars(String path) throws DocumentException {
        String rootPath = StringUtils.isBlank(path) ? this.libPath : path;
        //递归加载仓库中所有jar包（此处不按照pom文件加载是因为 pom文件中api还可能依赖其他包，但是该包不在pom引用中）
        File jarPath = new File(rootPath);
        File[] jarFiles = jarPath.listFiles();
        if(jarFiles != null && jarFiles.length > 0){
            for (File file : jarFiles) {
                if(file.isDirectory()){
                    loadJars(file.getPath());
                } else {
                    String name = file.getName();
                    if(name.endsWith(".jar") || name.endsWith(".zip")){
                        loadClass(file);
                    }
                }
            }
        } 
        /*List<PomModel> dependencies = listDependencies(new PomDTO());
        dependencies.forEach(model -> {
            try{
                rootPath.append(model.getGroupId().replace(".", File.separator)).append(File.separator);
                rootPath.append(model.getArtifactId()).append(File.separator).append(model.getVersion());
                // 系统类库路径
                File jarPath = new File(libPath);
                if(jarPath.exists()){
                 // 获取所有的.jar和.zip文件
                    File[] jarFiles = jarPath.listFiles((dir, name) -> name.endsWith(".jar") || name.endsWith(".zip"));
                    if (jarFiles != null) {
                        // 从URLClassLoader类中获取类所在文件夹的方法
                        // 对于jar文件，可以理解为一个存放class文件的文件夹
                        Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
                        boolean accessible = method.isAccessible();     // 获取方法的访问权限
                        try {
                            if (!accessible) {
                                method.setAccessible(true);     // 设置方法的访问权限
                            }
                            // 获取系统类加载器
                            URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
                            for (File file : jarFiles) {
                                URL url = file.toURI().toURL();
                                try {
                                    method.invoke(classLoader, url);
                                    log.debug("读取jar文件[name={}]", file.getName());
                                } catch (Exception e) {
                                    log.error("读取jar文件[name={}]失败", file.getName());
                                }
                            }
    
                        } finally {
                            method.setAccessible(accessible);
                        }
                    } 
                }
            } catch (Exception e){
                log.error("load jars occur an error:{}", e);
            }
        });*/
        return ResultDTO.createSuccessResult("load jars completely and successfully", String.class);
    }

    private void loadClass(File file) {
        // 从URLClassLoader类中获取类所在文件夹的方法
        // 对于jar文件，可以理解为一个存放class文件的文件夹
        try {
            Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
            boolean accessible = method.isAccessible(); // 获取方法的访问权限
            try {
                if (!accessible) {
                    method.setAccessible(true); // 设置方法的访问权限
                }
                // 获取系统类加载器
                URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
                URL url = file.toURI().toURL();
                try {
                    method.invoke(classLoader, url);
                    log.debug("读取jar文件[name={}]", file.getName());
                } catch (Exception e) {
                    log.error("读取jar文件[name={}]失败", file.getName());
                }

            } finally {
                method.setAccessible(accessible);
            }
        } catch (Exception e) {
            log.error("加载类出错", e);
        }

    }

}
